package com.bocek.chakmapoint.model;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Stroke;
import java.awt.geom.Rectangle2D;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;

/**
 * The text implementation of the SlideItem.
 * 
 * @author Omer Gurlek
 * @version 1.0
 */
public class CText implements SlideItem {

	private boolean selected;
	private String name;
	private String value;
	private Color color;
	private Font font;
	private int thickness = 0;
	private Point position;

	private double minXBound;
	private double maxXBound;
	private double minYBound;
	private double maxYBound;

	/**
	 * Default constructor..
	 * 
	 * @requires Nothing.
	 * @modifies All except the bound variables.
	 * @effects Returns a new CText object.
	 */
	public CText() {
		this.selected = false;
		this.name = "Text";
		this.value = "";
		this.color = Color.black;
		this.font = new Font("Verdana", Font.PLAIN, 10);
		this.position = new Point(0, 0);
	}

	public boolean repOK() {
		boolean ok = true;

		ok &= (name != null);
		ok &= (value != null);
		ok &= (color != null);
		ok &= (font != null);
		ok &= (position != null);
		ok &= (thickness > 0);

		return ok;
	}

	/**
	 * Changes the font with 3 parameters.
	 * 
	 * @requires Non-null parameters: font type String, font style Integer and
	 *           font size Integer.
	 * @modifies The font variable.
	 * @effects The font of the CText is changed.
	 */
	public void changeFont(String type, int style, int size) {
		this.font = new Font(type, style, size);
	}

	/**
	 * Changes the Font Type
	 * 
	 * @requires Non-null font type String parameter.
	 * @modifies The font variable.
	 * @effects The font type of the CText is changed.
	 */
	public void changeFontType(String fontType) {
		this.font = new Font(fontType, this.font.getStyle(),
				this.font.getSize());
	}

	/**
	 * Changes the Font Size
	 * 
	 * @requires Non-null font size Integer parameter.
	 * @modifies The font variable.
	 * @effects The font size of the CText is changed.
	 */
	public void changeFontSize(int size) {
		this.font = new Font(this.font.getName(), this.font.getStyle(), size);
	}

	/**
	 * Changes the Font Style
	 * 
	 * @requires Non-null font style Integer parameter.
	 * @modifies The font variable.
	 * @effects The font style of the CText is changed.
	 */
	public void changeFontStyle(int style) {
		this.font = new Font(this.font.getName(), style, this.font.getSize());
	}

	/**
	 * Refreshes the values of the actual pixel boundaries of the text.
	 * 
	 * @requires Nothing.
	 * @modifies Bound variables: minXBound, maxXBound, minYBound, maxYBound.
	 * @effects The pixel boundaries of the text are set.
	 */
	public void refreshBounds() {
		FontMetrics metrics = new FontMetrics(this.font) {
		};
		Rectangle2D bounds = metrics.getStringBounds(this.value, null);

		double height = bounds.getHeight();
		double width = bounds.getWidth();

		this.minXBound = this.position.getX();
		this.minYBound = this.position.getY() - 8;

		this.maxYBound = height + minYBound;
		this.maxXBound = width + minXBound;
	}

	@Override
	public void draw(Graphics g) {
		Graphics2D g2d = (Graphics2D) g;
		Color previousColor = g2d.getColor();
		Stroke previousStroke = g2d.getStroke();
		Font previousFont = g2d.getFont();

		g2d.setColor(this.color);
		g2d.setStroke(new BasicStroke(this.thickness));
		g2d.setFont(this.font);

		g2d.drawString(this.value, (int) this.position.getX(),
				(int) this.position.getY());

		if (isSelected()) {
			// dashed line
			g2d.setStroke(new BasicStroke(2, BasicStroke.CAP_ROUND,
					BasicStroke.JOIN_ROUND, 0.0f, new float[] { 4.0f, 4.0f },
					0.9f));
			g2d.setColor(Color.BLUE);
			g2d.draw(this.drawSelection());
		}

		g2d.setStroke(previousStroke);
		g2d.setColor(previousColor);
		g2d.setFont(previousFont);
	}

	@Override
	public boolean isPointInside(Point p) {
		this.refreshBounds();

		double clickX = p.getX();
		double clickY = p.getY();

		if ((clickX >= minXBound) && (clickX <= maxXBound)
				&& (clickY >= minYBound) && (clickY <= maxYBound)) {
			return true;
		} else {
			return false;
		}
	}

	@Override
	public boolean select(Point p) {
		if (isPointInside(p)) {
			this.setSelected(true);
			return true;
		} else {
			this.setSelected(false);
			return false;
		}
	}

	@Override
	public boolean deselect() {
		this.setSelected(false);
		return true;
	}

	@Override
	public boolean isSelected() {
		return this.selected;
	}

	@Override
	public void move(Point delta) {
		position.move((int) (position.getX() + delta.getX()),
				(int) (position.getY() + delta.getY()));
		this.refreshBounds();
	}

	@Override
	public boolean isOutsideFrame(Dimension frameDimension) {
		this.refreshBounds();

		double height = frameDimension.getHeight();
		double width = frameDimension.getWidth();

		if ((this.minXBound >= 0) && (this.minYBound >= 0)
				&& (this.maxXBound <= width) && (this.maxYBound <= height)) {
			return false;
		} else {
			return true;
		}
	}

	public boolean intersects(Rectangle r) {
		int x = (int) position.getX();
		int y = (int) position.getY();

		Point topLeft = new Point(x, y);
		Point topRight = new Point(x, y);
		Point bottomRight = new Point(x, y);
		Point bottomLeft = new Point(x, y);

		topRight.move((int) (maxXBound - minXBound), 0);
		bottomRight.move((int) (maxXBound - minXBound),
				(int) (maxYBound - minYBound));
		bottomLeft.move(0, (int) (maxYBound - minYBound));

		if (r.contains(topLeft) || r.contains(topRight)
				|| r.contains(bottomLeft) || r.contains(bottomRight)) {
			return true;
		} else {
			return false;
		}
	}

	public String getFontType() {
		return this.font.getFamily();
	}

	public int getFontStyle() {
		return this.font.getStyle();
	}

	public boolean isItalic() {
		int fontStyle = getFontStyle();
		return ((fontStyle ^ Font.ITALIC) != fontStyle);
	}
	
	public boolean isBold() {
		int fontStyle = getFontStyle();
		return ((fontStyle ^ Font.BOLD) != fontStyle);
	}	

	public int getFontSize() {
		return this.font.getSize();
	}

	@Override
	public String getName() {
		return this.name;
	}

	public void setFont(Font font) {
		this.font = font;
	}

	public Font getFont() {
		return font;
	}

	public String getValue() {
		return this.value;
	}

	public void setValue(String value) {
		this.value = value;
	}

	public Color getColor() {
		return this.color;
	}

	public void setColor(Color color) {
		this.color = color;
	}

	public int getThickness() {
		return this.thickness;
	}

	public void setThickness(int thickness) {
		this.thickness = thickness;
	}

	public void setSelected(boolean selected) {
		this.selected = selected;
	}

	@Override
	public Rectangle drawSelection() {
		Point s = new Point((int) this.minXBound, (int) this.minYBound);
		Point e = new Point((int) this.maxXBound, (int) this.maxYBound);

		Rectangle r = new Rectangle((int) s.getX(), (int) s.getY(),
				(int) (e.getX() - s.getX()), (int) (e.getY() - s.getY()));
		r.setBounds(r.x - 3, r.y - 3, r.width + 6, r.height + 4);
		return r;
	}
	
	@Override
	public Element saveToXML(Document doc, int depth) throws ParserConfigurationException {
		/*
		<textbox font="" fontSize="12" isItalic="true" isBold="false" color="" url="" depth="7" xCoord="1" yCoord="3">
						something like this
		</textbox>
		*/
		
		// DEFAULT
		
		Element e = doc.createElement("textbox");
		e.setAttribute("font", this.getFontType());
		e.setAttribute("fontSize", Integer.toString(this.getFontSize()));
		e.setAttribute("isItalic", Boolean.toString(this.isItalic()));
		e.setAttribute("isBold", Boolean.toString(this.isBold()));
		
		e.setAttribute("url", this.value);
		
		e.setAttribute("depth", Integer.toString(depth));
		e.setAttribute("xCoord", Integer.toString((int) this.minXBound));
		e.setAttribute("yCoord", Integer.toString((int) this.minYBound));
		
		e.appendChild(doc.createTextNode(this.value));
		
		return e;
	}

	@Override
	public void fill() {
		// TODO Auto-generated method stub
		
	}
}
